// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "runtime_types.h"
#include "type.h"
#include "slice.h"
#include "mem_alloc.h"
#include "assert.h"
#include "panic.h"

struct __go_open_array __go_make_slice1(const struct __go_type_descriptor *td, uintptr len)
{
	return __go_make_slice2(td, len, len);
}

struct __go_open_array __go_make_slice1_big(const struct __go_type_descriptor *td, uint64 len)
{
	return __go_make_slice2_big(td, len, len);
}

struct __go_open_array __go_make_slice2(const struct __go_type_descriptor *td, uintptr len, uintptr cap)
{
	const struct __go_slice_type *std;
	int ilen;
	int icap;
	uintptr size;
	struct __go_open_array ret;
	unsigned int flag;

	__go_assert(td->__code == GO_SLICE);
	std = (const struct __go_slice_type *)td;

	ilen = (int)len;
	if (ilen < 0 || (uintptr) ilen != len)
		runtime_panicstring("makeslice: len out of range");

	icap = (int)cap;
	if (cap < len || (uintptr) icap != cap ||
			(std->__element_type->__size > 0 &&
			cap > (uintptr) - 1U / std->__element_type->__size))
		runtime_panicstring("makeslice: cap out of range");

	ret.__count = ilen;
	ret.__capacity = icap;

	size = cap * std->__element_type->__size;
	flag = ((std->__element_type->__code & GO_NO_POINTERS) != 0 ? FlagNoPointers : 0);
	ret.__values = runtime_mallocgc(size, flag, 1, 1);

	return ret;
}

struct __go_open_array __go_make_slice2_big(const struct __go_type_descriptor *td, uint64 len, uint64 cap)
{
	uintptr slen;
	uintptr scap;

	slen = (uintptr) len;
	if ((uint64) slen != len)
		runtime_panicstring("makeslice: len out of range");

	scap = (uintptr) cap;
	if ((uint64) scap != cap)
		runtime_panicstring("makeslice: cap out of range");

	return __go_make_slice2(td, slen, scap);
}


// The builtin Go append function.

struct __go_open_array __go_append(struct __go_open_array a, void *bvalues, uintptr bcount, uintptr element_size)
{
	uintptr ucount;
	int count;

	if (bvalues == NULL || bcount == 0)
		return a;

	ucount = (uintptr) a.__count + bcount;
	count = (int)ucount;
	if ((uintptr) count != ucount || count <= a.__count)
		runtime_panicstring("append: slice overflow");

	if (count > a.__capacity) {
		int m;
		void *n;

		m = a.__capacity;
		if (m == 0)
			m = (int)bcount;
		else {
			do {
				if (a.__count < 1024)
					m += m;
				else
					m += m / 4;
			}
			while (m < count);
		}

		n = runtime_malloc(m * element_size);
		__builtin_memcpy(n, a.__values, a.__count * element_size);

		a.__values = n;
		a.__capacity = m;
	}

	__builtin_memmove((char *)a.__values + a.__count * element_size, bvalues, bcount * element_size);
	a.__count = count;
	return a;
}
